﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Caching;
using System.Web.UI;
using System.IO;

namespace MvcShop.Extensions.Caching
{
    public static class CacheExtensions
    {
        public static string Cache(
            this HtmlHelper htmlHelper,
            string cacheKey,
            CacheDependency cacheDependencies,
            DateTime absoluteExpiration,
            TimeSpan slidingExpiration,
            Func<object> func)
        {
            var cache = htmlHelper.ViewContext.HttpContext.Cache;
            var content = cache.Get(cacheKey) as string;

            if (content == null)
            {
                content = func().ToString();
                cache.Insert(cacheKey, content, cacheDependencies, absoluteExpiration, slidingExpiration);
            }

            return content;
        }

        private static readonly string RECORD_WRITER_KEY = Guid.NewGuid().ToString();

        private static void SetRecordWriter(this ExHtmlHelper htmlHelper, RecordWriter writer)
        {
            var exist = htmlHelper.ViewPage.Items[RECORD_WRITER_KEY] as RecordWriter;
            if (exist != null)
            {
                throw new InvalidOperationException(
                    "the Cache writer is existing. " + 
                    "Maybe the base page is already cache enabled.");
            }

            htmlHelper.ViewPage.Items[RECORD_WRITER_KEY] = writer;
        }

        private static RecordWriter GetRecordWriter(this ExHtmlHelper htmlHelper)
        {
            var writer = htmlHelper.ViewPage.Items[RECORD_WRITER_KEY] as RecordWriter;
            if (writer == null)
            {
                throw new InvalidOperationException(
                    "Cache writer is missing. Please render to the wrapping " +
                    "writer create by CreateCacheWriter method.");
            }

            return writer;
        }

        public static TextWriter CreateCacheWriter(this ExHtmlHelper htmlHelper, TextWriter writer)
        {
            var recordWriter = new RecordWriter(writer);
            htmlHelper.SetRecordWriter(recordWriter);
            return recordWriter;
        }

        public static void Cache(
            this ExHtmlHelper htmlHelper,
            string cacheKey,
            DateTime absoluteExpiration,
            Action action)
        {
            htmlHelper.Cache(
                cacheKey,
                null,
                absoluteExpiration,
                System.Web.Caching.Cache.NoSlidingExpiration,
                action);
        }

        public static void Cache(
            this ExHtmlHelper htmlHelper,
            string cacheKey,
            CacheDependency cacheDependencies,
            DateTime absoluteExpiration,
            TimeSpan slidingExpiration,
            Action action)
        {
            var cache = htmlHelper.ViewContext.HttpContext.Cache;
            var content = cache.Get(cacheKey) as string;
            var writer = htmlHelper.GetRecordWriter();

            if (content == null)
            {
                var recorder = new StringBuilder();
                writer.AddRecorder(recorder);

                action();

                writer.RemoveRecorder(recorder);
                content = recorder.ToString();
                cache.Insert(cacheKey, content, cacheDependencies, absoluteExpiration, slidingExpiration);
            }
            else
            {
                htmlHelper.Output.Write(content);
            }
        }
    }
}
